package com.emeraldparser.symbol;

import com.emeraldparser.util.Automaton;
import com.emeraldparser.util.Automatons;
import com.emeraldparser.util.Characters;
import com.emeraldparser.util.HashCodes;

public final class CharacterRangeSymbolClass extends TwoStateSymbolClass {

	private final char lowerBound;
	private final char upperBound;
	private final transient int hashCode;

	public CharacterRangeSymbolClass(char lowerBound, char upperBound) {
		this.lowerBound = lowerBound;
		this.upperBound = upperBound;
		this.hashCode = HashCodes.classSpecificHashCode(CharacterRangeSymbolClass.class, lowerBound, upperBound);
	}

	public char getLowerBound() {
		return lowerBound;
	}

	public char getUpperBound() {
		return upperBound;
	}

	@Override
	public String toString() {
		return "('" + Characters.escape(lowerBound) + "'..'" + Characters.escape(upperBound) + "')";
	}

	@Override
	public Automaton<? extends State, ? extends SymbolClass> toAutomaton() {
		Automaton<State, SymbolClass> automaton = Automatons.create(super.toAutomaton());
		
		for (char character = lowerBound; character <= upperBound; character++) {
			automaton.addDirectedEdge(getBeforeState(), getAfterState(), new CharacterSymbolClass(character));
		}
		
		return automaton;
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (getClass() != obj.getClass()) {
			return false;
		}
		
		CharacterRangeSymbolClass other = (CharacterRangeSymbolClass) obj;
		
		return lowerBound == other.lowerBound
		    && upperBound == other.upperBound;
	}

	@Override
	public <I, O> O accept(SymbolClassVisitor<I, O> visitor, I input) {
		return visitor.visitCharacterRangeSymbolClass(this, input);
	}

}
